Delphi Sorted tListView - Felix John COLIBRI. |
- abstract : a tListview sortable by clicking on the header, with string / numeric justification and column sizing
- key words : tListView, sorting, column sizing
- software used : Windows 10 64 bit, Delphi 7
- hardware used : intel i3-8100, 3.6 gHz, 8GB memory, 128 G and 1 T hard disc
- scope : Delphi 1 to 7, 2006 to 2010, Xe
- level : Delphi developer
- plan :
1 - Sorting a tListView This component will enable the sorting of the rows of a tListView by clicking
on the column header. It allows - automatic sortingthe columns by clicking on the header
- left or right justification
- sizing of columns
2 - Delphi Sortable tListView 2.1 - The Delphi tListView The Delphi tListView is an encapsulation of the Windows ListView control.
It contains some of the features of the tListBox like Items, TopItem, Clear. But it has many other possibilities, like resizing of the Items, custom drawing etc
2.2 - Sorting a tListView by column
To sort by ²colum, the tListView has - an AlphaSort method
- which requires the writing of an OnCompare event
Whenevet AlphSort requires the comparison of 2 rows, it calls OnCompare where
we tell which row is greater or lower than the other.
In our case, our main goal was to compare file information: file name, file size, file date. Therefore we wrote the OnCompare event for those type of values.
2.3 - The sorted listview component To manage each column, we use an array of t_column_info Records:
Type t_sort_kind= (e_not_sorted, e_ascending, e_descending);
t_column_info= Record
m_column_type: (e_unknown_type, e_string_type, e_integer_type);
m_sort_kind: t_sort_kind;
End; | and : - t_sort_kind stores the sort kind, which enables the toggling between ascending and descending
- we only need two kind of type :
- String for the names (file name, extension, path) and even for dates (our dates are always in string-sortable order: "yyyy-mm-dd hh-nn_ss")
- Integer for the file size
The tListView encapsulation is then Type c_sorted_listview=
Class(tListView) Public
m_name: String;
m_total_width: Integer;
m_clicked_column: Integer;
m_column_info_array: Array Of t_column_info;
m_c_result_list: tStringList;
Constructor create_sorted_listview(p_name: String;
p_c_owner: tComponent);
Procedure add_column(p_title: String; p_width: Integer; p_type: String);
Procedure add_value(p_value_array: Array Of String);
Procedure handle_listview_compare(Sender: TObject;
Item1, Item2: TListItem; Data: Integer; Var Compare: Integer);
// -- dispatch handler
Procedure ColClick(Column: TListColumn); Override;
Procedure Click; Override;
Destructor Destroy; Override;
End; // c_sorted_listview | where:
- m_column_info_array keeps track of each column's information
- m_total_width is used to adjust the size of the last column to the remaining size at the right
- m_c_result_list is used to store a textual representation of the tListView (the text can be displayed in a tMemo and then easily copied and pasted)
add_column is used to create the tListViews columns. Here is an example:
With g_c_sorted_listview Do Begin
add_column('name', 70, '');
add_column('-number', 50, 'i');
add_column('path', -1, '');
End; // with g_c_sorted_listview do | and the code is:
Procedure c_sorted_listview.add_column(p_title: String; p_width: Integer;
p_type: String);
Var l_info_array_index: Integer; Begin
With Columns.Add Do
Begin
If (Length(p_title)> 0) And (p_title[1]= '-')
Then Begin
System.Delete(p_title, 1, 1);
Caption:= p_title;
Alignment:= taRightJustify;
End
Else Caption:= p_title;
If p_width< 0
Then Begin
Width:= Self.Width- 15;
End Else Begin
Width:= p_width;
Inc(m_total_width, p_width);
End; End; // with file_listview_
l_info_array_index:= Length(m_column_info_array);
SetLength(m_column_info_array, l_info_array_index+ 1);
With m_column_info_array[l_info_array_index] Do
If p_type= ''
Then m_column_type:= e_string_type
Else
If p_type= 'i'
Then m_column_type:= e_integer_type
Else
If p_type= 'd'
Then m_column_type:= e_double_type;
End; // add_column | where - the first parameter is the caption of the column, with an optional "-" to
spedify right justification (for numbers). The default is left justification
- the second parameter is the requires width. "-1" is used to set the width to the remaining width for the last column
- the default type is String, and the "i" type refers to Integer
To fill a tListView row, we use add_value. Here is a simple example:
With g_c_sorted_listview Do
Begin Clear;
_add('bbb', '10', 'dddddd');
_add('www', '2', 'aaa');
_add('eee', '55', 'nnn');
End; // with g_c_sorted_listview | and the code is
Procedure c_sorted_listview.add_value(p_value_array: Array Of String);
Var l_value_index: Integer; Begin
With Items.Add Do Begin
If High(p_value_array)> 0
Then Caption:= p_value_array[0];
For l_value_index:= 1 To High(p_value_array) Do
SubItems.Add(p_value_array[l_value_index]);
End; // with Items.Add End; // add_value |
The special handling of the 0 value is required by the way Delphi fills a tListView row.
To trigger a sort, the user clicks on some column header. This triggers the
ColClick dispatcher, which we override like this:
Procedure c_sorted_listview.ColClick(Column: TListColumn);
Begin m_clicked_column:= Column.Index;
If m_column_info_array[m_clicked_column].m_sort_kind
In [e_not_sorted, e_descending]
Then m_column_info_array[m_clicked_column].m_sort_kind:= e_ascending
Else m_column_info_array[m_clicked_column].m_sort_kind:= e_descending;
AlphaSort; End; // ColClick |
AlphaSort then calls, whenever required by the sorting algorithm, our OnCompare event:
Procedure c_sorted_listview.handle_listview_compare(Sender: TObject;
Item1, Item2: TListItem; Data: Integer; Var Compare: Integer);
Var l_direction: Integer;
l_size_1, l_size_2: Integer;
l_item_1, l_item_2: String; Begin
If m_column_info_array[m_clicked_column].m_sort_kind= e_ascending
Then l_direction:= 1
Else l_direction:= -1;
If m_clicked_column= 0 Then Begin
l_item_1:= Item1.Caption;
l_item_2:= Item2.Caption;
End Else Begin
l_item_1:= Item1.SubItems[m_clicked_column- 1];
l_item_2:= Item2.SubItems[m_clicked_column- 1];
End;
Case m_column_info_array[m_clicked_column].m_column_type Of
e_string_type :
Compare:= l_direction* CompareText(l_item_1, l_item_2);
e_integer_type : Begin
l_size_1:= StrToInt(l_item_1);
l_size_2:= StrToInt(l_item_2);
If l_size_1< l_size_2
Then Compare:= -1
Else Compare:= 1;
Compare := l_direction* Compare;
End; End; // case
End; // ListView1Compare |
We also added of displaying a textual representation of the tListView along
with the selected row count. Here is the code:
Procedure c_sorted_listview.Click;
Var l_list_index: Integer;
l_selected_count: Integer;
l_display: String; Begin
m_c_result_list.Free;
m_c_result_list:= tStringList.Create;
With Items Do Begin
l_selected_count:= 0;
For l_list_index:= 0 To Count- 1 Do
With Items[l_list_index] Do
Begin l_display:= Caption;
m_c_result_list.Add(l_display);
If Selected
Then Inc(l_selected_count);
End; // with file_listview_, Items
With m_c_result_list Do
m_c_result_list.Insert(0, Format('// count %d sel %d', [Items.Count, l_selected_count]));
End; // Click | Obviously the code here is very much application dependent.
Finally the Constructor is:
Constructor c_sorted_listview.create_sorted_listview(p_name: String;
p_c_owner: tComponent); Begin
Inherited Create(p_c_owner);
m_name:= p_name;
Parent:= p_c_owner As tWinControl;
Align:= alClient; ViewStyle:= vsReport;
HideSelection:= False; MultiSelect:= True;
OnCompare:= handle_listview_compare; End; // create_sorted_listview
|
2.4 - Simple tListView sorting example In this example - we create the sorted tListView with
Procedure TForm1.create_Click(Sender: TObject);
Begin g_c_sorted_listview.Free;
g_c_sorted_listview:= c_sorted_listview.create_sorted_listview('',
listview_panel);
With g_c_sorted_listview Do Begin
add_column('name', 70, '');
add_column('-number', 50, 'i');
add_column('path', -1, ''); Clear;
_add('bbb', '10', 'dddddd');
_add('www', '2', 'aaa');
_add('eee', '55', 'nnn');
End; // with g_c_sorted_listview End; // create_click |
- and we display the result of some row clicks with
Procedure TForm1.display_rows_Click(Sender: TObject);
Begin If (g_c_sorted_listview<> Nil)
And (g_c_sorted_listview.m_c_result_list<> Nil)
Then
With g_c_sorted_listview Do
display_memo_.Lines.Assign(m_c_result_list)
End; // display_rows_Click | Here is the snapshot of the example (after clickin on the number column and selecting 2 rows):
3 - Download the Sources Here are the source code files: The .ZIP file(s) contain:
- the main program (.DPR, .DOF, .RES), the main form (.PAS, .DFM), and any other auxiliary form
- any .TXT for parameters, samples, test data
- all units (.PAS) for units
Those .ZIP
- are self-contained: you will not need any other product (unless expressly mentioned).
- for Delphi 6 projects, can be used from any folder (the pathes are RELATIVE)
- will not modify your PC in any way beyond the path where you placed the .ZIP (no registry changes, no path creation etc).
To use the .ZIP: - create or select any folder of your choice
- unzip the downloaded file
- using Delphi, compile and execute
To remove the .ZIP simply delete the folder. The Pascal code uses the Alsacian notation, which prefixes identifier by
program area: K_onstant, T_ype, G_lobal, L_ocal, P_arametre, F_unction, C_lass etc. This notation is presented in the Alsacian Notation paper.
As usual:
- please tell us at fcolibri@felix-colibri.com if you found some errors, mistakes, bugs, broken links or had some problem downloading the file. Resulting corrections will
be helpful for other readers
- we welcome any comment, criticism, enhancement, other sources or reference suggestion. Just send an e-mail to fcolibri@felix-colibri.com.
- or more simply, enter your (anonymous or with your e-mail if you want an answer) comments below and clic the "send" button
- and if you liked this article, talk about this site to your fellow developpers, add a link to your links page ou mention our articles in your blog or newsgroup posts when relevant. That's the way we operate:
the more traffic and Google references we get, the more articles we will write.
4 - The author Felix John COLIBRI works at the Pascal
Institute. Starting with Pascal in 1979, he then became involved with Object Oriented Programming, Delphi, Sql, Tcp/Ip, Html, UML. Currently, he is mainly
active in the area of custom software development (new projects, maintenance, audits, BDE migration, Delphi
Xe_n migrations, refactoring), Delphi Consulting and Delph
training. His web site features tutorials, technical papers about programming with full downloadable source code, and the description and calendar of forthcoming Delphi, FireBird, Tcp/IP, Web Services, OOP / UML, Design Patterns, Unit Testing training sessions. |